ProfileHeader.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. "use client";
  2. import { GameListRep, GameRequest } from "@/api/home";
  3. import { userInfoApi } from "@/api/login";
  4. import {
  5. UserInfoRep,
  6. UserVipInfo,
  7. Wallet,
  8. cleanBounsApi,
  9. getUserMoneyApi,
  10. getUserTransferApi,
  11. } from "@/api/user";
  12. import {
  13. BalanceContent,
  14. BonusContent,
  15. FreeContent,
  16. ReplayContent,
  17. } from "@/components/ModalPopup/WalletDescribeModal";
  18. import TipsModal, { ModalProps } from "@/components/TipsModal";
  19. import useGame from "@/hooks/useGame";
  20. import { Link, useRouter } from "@/i18n/routing";
  21. import { useWalletStore } from "@/stores/useWalletStore";
  22. import { WalletEnum } from "@/types";
  23. import { vipImages } from "@/utils/constant";
  24. import { flatPoint, percentage } from "@/utils/methods";
  25. import { Badge, Button, Mask, ProgressBar, Toast } from "antd-mobile";
  26. import { useTranslations } from "next-intl";
  27. import Image from "next/image";
  28. import { Fragment, useEffect, useRef, useState } from "react";
  29. type Props = {
  30. userInfo: UserInfoRep;
  31. userMoney: Wallet;
  32. userVip: UserVipInfo;
  33. };
  34. const VipCard = (props: { userVip: UserVipInfo }) => {
  35. const { userVip } = props;
  36. const t = useTranslations("ProfilePage");
  37. // Vip 图标
  38. const vipIconElement = vipImages.map((item, index) => {
  39. if (item.leve === userVip?.vip_level) {
  40. return (
  41. <Fragment key={index}>
  42. <Image src={item.src} alt={"vip"} height={110} width={100} />
  43. <span className={"icon-level"} style={{ color: item.color }}>
  44. {item.leve}
  45. </span>
  46. </Fragment>
  47. );
  48. }
  49. });
  50. if (!userVip) return null;
  51. return (
  52. <div className={"vip-card"}>
  53. <div className={"vip-card__icon"}>{vipIconElement}</div>
  54. <div className={"vip-card-process"}>
  55. {/*<div className={"process-top"}>{userVip.vip_exp}xp</div>*/}
  56. <div>
  57. <ProgressBar
  58. percent={percentage(userVip?.vip_exp, userVip?.vip_score_exp)}
  59. style={{
  60. "--fill-color": "#bd37e0",
  61. "--track-color": "#65609b",
  62. "--track-width": "0.0694rem",
  63. }}
  64. />
  65. </div>
  66. <div className={"process-bottom text-[#e581ff]"}>
  67. <span>VIP{userVip?.vip_level}</span>
  68. <span className={"process-bottom-desc"}>
  69. {t("expTips", {
  70. exp: flatPoint(userVip.vip_score_exp - userVip.vip_exp),
  71. })}
  72. </span>
  73. <span>
  74. VIP
  75. {userVip.vip_next_level}
  76. </span>
  77. </div>
  78. </div>
  79. </div>
  80. );
  81. };
  82. const WalletCard = (props: { userMoney: Wallet }) => {
  83. const { userMoney } = props;
  84. const t = useTranslations("ProfilePage");
  85. const tcdoe = useTranslations();
  86. const tipsRef = useRef<ModalProps>(null);
  87. const [tipsStatus, setTipsStatus] = useState<keyof typeof WalletEnum>("Bonus");
  88. const modalHandler = (key: keyof typeof WalletEnum) => {
  89. setTipsStatus(key);
  90. tipsRef.current?.onOpen();
  91. };
  92. // 未完成游戏
  93. const gameModelRef = useRef<ModalProps>(null);
  94. const game = useRef<(GameListRep & { mode: GameRequest["mode"] }) | null>(null);
  95. // 彩金、免费币、重玩币提现到钱包操作
  96. const handleAcquire = async (wallet_type: number, transfer: boolean) => {
  97. if (!transfer) return;
  98. // 先判断是否有未完成的游戏
  99. const { data }: any = await userInfoApi();
  100. // 如果有未完成游戏 彩金游戏-2、免费游戏-3、重玩游戏-4
  101. if (wallet_type === 2 && data.play_list && data.play_list.length > 0) {
  102. game.current = data.play_list[0];
  103. game.current!.mode = 1;
  104. gameModelRef.current?.onOpen();
  105. return;
  106. }
  107. if (wallet_type === 3 && data.free_game_list && data.free_game_list.length > 0) {
  108. game.current = data.free_game_list[0];
  109. gameModelRef.current?.onOpen();
  110. game.current!.mode = 2;
  111. return;
  112. }
  113. if (wallet_type === 4 && data.lose_game_list && data.lose_game_list.length > 0) {
  114. game.current = data.lose_game_list[0];
  115. game.current!.mode = 3;
  116. gameModelRef.current?.onOpen();
  117. return;
  118. }
  119. getUserTransferApi({ wallet_type })
  120. .then((res) => {
  121. if (res.code === 200) {
  122. Toast.show(tcdoe("code.200"));
  123. setTimeout(() => {
  124. tipsRef.current?.onClose();
  125. }, 1000);
  126. }
  127. })
  128. .catch((error) => {
  129. Toast.show(tcdoe(`code.${error.data.code}`));
  130. });
  131. };
  132. const { getGameUrl } = useGame();
  133. const goGame = () => {
  134. const current = game.current;
  135. getGameUrl(current!, { id: current?.id + "", mode: game.current?.mode! });
  136. tipsRef.current?.onClose();
  137. gameModelRef.current?.onClose();
  138. };
  139. return (
  140. <>
  141. <TipsModal
  142. className="walletModal"
  143. ref={tipsRef}
  144. title={
  145. <div className={"flex items-center text-[#00f6ff]"}>
  146. <i
  147. className={"iconfont icon-liwuhuodong mr-[0.0694rem] text-[0.2778rem]"}
  148. ></i>
  149. {t("modalTitle")}
  150. </div>
  151. }
  152. >
  153. {/*现金*/}
  154. {tipsStatus === WalletEnum.Balance ? <BalanceContent wallet={userMoney} /> : null}
  155. {/* 彩金*/}
  156. {tipsStatus === WalletEnum.Bonus ? (
  157. <BonusContent wallet={userMoney} handleAcquire={handleAcquire} />
  158. ) : null}
  159. {/* 免费币 */}
  160. {tipsStatus === WalletEnum.Free ? (
  161. <FreeContent wallet={userMoney} handleAcquire={handleAcquire} />
  162. ) : null}
  163. {/* 重玩币 */}
  164. {tipsStatus === WalletEnum.Replay ? (
  165. <ReplayContent wallet={userMoney} handleAcquire={handleAcquire} />
  166. ) : null}
  167. </TipsModal>
  168. {/* 提现拦截 */}
  169. <TipsModal title={"Tips"} ref={gameModelRef}>
  170. <p className={"text-left text-[0.12rem] font-medium text-[#666]"}>
  171. Existem jogos de bônus pendentes que não podem iniciar a retirada.
  172. </p>
  173. <div className={"mt-[0.0694rem] flex justify-center"}>
  174. <Button
  175. color={"primary"}
  176. className={"mx-auto"}
  177. style={{
  178. "--background-color": "var(--primary-color)",
  179. "--border-color": "var(--primary-color)",
  180. }}
  181. onClick={goGame}
  182. >
  183. para jogos
  184. </Button>
  185. </div>
  186. </TipsModal>
  187. <div className="coin">
  188. <div className={"coin_right_wallet"}>
  189. <div
  190. className={"wallet_left_border"}
  191. onClick={() => modalHandler(WalletEnum.Balance)}
  192. >
  193. <div className="wallet-right-icon">
  194. <span className="coin_left__icon iconfont icon-qianbao3"></span>
  195. </div>
  196. <section>
  197. <div className={"wallet_header"}>
  198. <span>{t("balance")}</span>
  199. {/* <Image
  200. className="wallet_header__icon"
  201. src="/img/a.png"
  202. alt="question"
  203. width={15}
  204. height={15}
  205. /> */}
  206. <div className="wallet-question">?</div>
  207. </div>
  208. <div className="num">
  209. <span className="uppercase">brl </span>
  210. <span>{userMoney.score || 0.0}</span>
  211. </div>
  212. </section>
  213. </div>
  214. <div
  215. className={"wallet_right_content"}
  216. onClick={() => modalHandler(WalletEnum.Bonus)}
  217. >
  218. <Badge
  219. content={userMoney.is_point_transfer ? Badge.dot : null}
  220. style={{ right: "10px" }}
  221. >
  222. <div className="wallet-right-icon">
  223. <span className="coin_left__icon iconfont icon-qianbao3"></span>
  224. </div>
  225. </Badge>
  226. <section>
  227. <div className={"wallet_header"}>
  228. {t("bonus")}
  229. <div className="wallet-question">?</div>
  230. </div>
  231. <div className="num">
  232. <span className="uppercase">brl </span>
  233. <span>{userMoney.point || 0.0}</span>
  234. </div>
  235. </section>
  236. </div>
  237. <div
  238. className={"wallet_left_border"}
  239. onClick={() => modalHandler(WalletEnum.Free)}
  240. >
  241. <Badge
  242. content={
  243. userMoney.is_free_transfer &&
  244. userMoney.free_score >= (userMoney?.free_transfer_min || 0)
  245. ? Badge.dot
  246. : null
  247. }
  248. style={{ right: "10px" }}
  249. >
  250. <div className="wallet-right-icon">
  251. <span className="coin_left__icon iconfont icon-qianbao3"></span>
  252. </div>
  253. </Badge>
  254. <section>
  255. <div className={"wallet_header"}>
  256. {t("free")}
  257. <div className="wallet-question">?</div>
  258. </div>
  259. <div className="num">
  260. <span className="uppercase">brl </span>
  261. <span>{userMoney.free_score || 0.0}</span>
  262. </div>
  263. </section>
  264. </div>
  265. <div
  266. className={"wallet_right_content"}
  267. onClick={() => modalHandler(WalletEnum.Replay)}
  268. >
  269. <Badge
  270. content={
  271. userMoney.is_lose_transfer &&
  272. userMoney.lose_score >= (userMoney?.lose_transfer_min || 0)
  273. ? Badge.dot
  274. : null
  275. }
  276. style={{ right: "10px" }}
  277. >
  278. <div className="wallet-right-icon">
  279. <span className="coin_left__icon iconfont icon-qianbao3"></span>
  280. </div>
  281. </Badge>
  282. <section>
  283. <div className={"wallet_header"}>
  284. {t("replay")}
  285. <div className="wallet-question">?</div>
  286. </div>
  287. <div className="num">
  288. <span className="uppercase">brl </span>
  289. <span>{userMoney.lose_score || 0.0}</span>
  290. </div>
  291. </section>
  292. </div>
  293. </div>
  294. </div>
  295. </>
  296. );
  297. };
  298. const NoBounsWarn = ({ visible, onClose, onConfirm }: any) => {
  299. const doClose = () => {
  300. if (typeof onClose === "function") onClose();
  301. };
  302. const doConfirm = () => {
  303. if (typeof onConfirm === "function") onConfirm();
  304. };
  305. return (
  306. <Mask visible={visible} onMaskClick={doClose}>
  307. <div className="absolute left-[50%] top-[40%] w-[80%] translate-x-[-50%]">
  308. <div className="relative rounded-[.1rem] border-[1px] border-[#e541ff] bg-gradient-to-b from-[#254cc5] to-[#4b179a] px-[.15rem] pb-[.15rem] pt-[0px] shadow-[0_-10px_15px_#7619b6_inset]">
  309. <div className="h-[.5rem] text-center">
  310. <img
  311. src="/warn_top.png"
  312. alt=""
  313. className="relative -top-[.3rem] inline-block w-[.9rem]"
  314. />
  315. </div>
  316. <div className="text-center text-[.12rem]">
  317. A demanda atual de fluxo de retirada de bônus é excessiva,Se esvaziar o
  318. bônus e retirar a demanda de água corrente?
  319. </div>
  320. <div className="mt-[.2rem] flex items-center justify-between px-[.1rem]">
  321. <button
  322. onClick={doConfirm}
  323. className="mr-[.1rem] h-[.35rem] flex-1 rounded-[60px] border border-[#e746ff] bg-[#703dc3] text-[.14rem] font-bold text-[#00f6ff] shadow-[0_10px_5px_#8f42d2_inset]"
  324. >
  325. A certeza
  326. </button>
  327. <button
  328. onClick={doClose}
  329. className="h-[.35rem] flex-1 rounded-[60px] border border-[#46e3ff] bg-[#6c3bc1] text-[.14rem] font-bold text-[#00f6ff] shadow-[0_-10px_5px_#4d4ebf_inset]"
  330. >
  331. Cancelar
  332. </button>
  333. </div>
  334. <i
  335. className="iconfont icon-guanbi absolute right-[.1rem] top-[.1rem]"
  336. onClick={doClose}
  337. ></i>
  338. </div>
  339. </div>
  340. </Mask>
  341. );
  342. };
  343. export const ProfileHeader = (props: Props) => {
  344. const { userInfo, userVip } = props;
  345. const t = useTranslations("ProfilePage");
  346. const { wallet, setWallet } = useWalletStore((state) => ({
  347. wallet: state.wallet,
  348. setWallet: state.setWallet,
  349. }));
  350. const router = useRouter();
  351. const [isShowNoBounsWarn, setIsShowNoBounsWarn] = useState(false);
  352. const [isShowed, setIsShowed] = useState(false);
  353. useEffect(() => {
  354. const curMul = wallet.target_point_rollover / ((wallet?.score || 0) + (wallet.point || 0));
  355. const config = wallet.no_bonus_config;
  356. if (curMul >= (config || 0) && !isShowed) {
  357. setIsShowNoBounsWarn(true);
  358. setIsShowed(true);
  359. }
  360. }, [wallet]);
  361. const handler = () => {
  362. if (!!wallet.score) {
  363. router.push("/withdraw");
  364. } else {
  365. Toast.show("no money ");
  366. }
  367. };
  368. const doConfirm = async () => {
  369. Toast.show({
  370. icon: "loading",
  371. maskClickable: false,
  372. });
  373. setIsShowNoBounsWarn(false);
  374. const res = await cleanBounsApi(1);
  375. if (res?.code && [6001, 6002].includes(res?.data?.code)) {
  376. const str = t(`bouns${res?.data?.code}`);
  377. Toast.show({
  378. content: str,
  379. icon: "fail",
  380. });
  381. }
  382. Toast.show({
  383. content: t("success"),
  384. icon: "success",
  385. });
  386. const walletRes = await getUserMoneyApi();
  387. setWallet(walletRes?.data);
  388. };
  389. return (
  390. <>
  391. <div className={"userContent"}>
  392. <div className={"userInfo"}>
  393. <div>
  394. <div className={"bgImg"}>
  395. <Image
  396. src={"/img/avatar.png"}
  397. className={"h-[100%] w-[100%]"}
  398. alt={"avatar"}
  399. width={120}
  400. height={120}
  401. />
  402. </div>
  403. <div>
  404. <span>{t("Conta")}</span>
  405. <span className="phone text-[#d3d1ff]">
  406. {userInfo?.user_phone || ""}
  407. </span>
  408. </div>
  409. </div>
  410. <Link
  411. className="goto iconfont icon-xiangzuo1 text-[#8883eb]"
  412. href={`/changePassword`}
  413. ></Link>
  414. </div>
  415. {/*vipcard*/}
  416. <VipCard userVip={userVip} />
  417. <WalletCard userMoney={wallet} />
  418. </div>
  419. <div className="link">
  420. <Link href={"/deposit"} className={"btn"}>
  421. <span> {t("Depósito")}</span>
  422. </Link>
  423. <p className={"btn"} onClick={handler}>
  424. <span>{t("Sacar")}</span>
  425. </p>
  426. </div>
  427. <NoBounsWarn
  428. visible={isShowNoBounsWarn} // 控制是否显示遮罩层,true 显示,false 不显示
  429. onClose={() => setIsShowNoBounsWarn(false)}
  430. onConfirm={doConfirm}
  431. ></NoBounsWarn>
  432. </>
  433. );
  434. };